package com.example.springbootdemo.processor;

import com.example.springbootdemo.config.Config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

@Slf4j
public class ComponentScanPostProcessor implements BeanFactoryPostProcessor {
  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    try {
      ComponentScan componentScan = AnnotationUtils.findAnnotation(Config.class, ComponentScan.class);
      AnnotationBeanNameGenerator nameGenerator = new AnnotationBeanNameGenerator();
      if (componentScan != null) {
        for (String base : componentScan.basePackages()) {
          log.info("basePackages: {}", base);
          String path = "classpath*:" + base.replace(".", "/") + "/**/*.class";
          log.info("path: {}", path);
          CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
          Resource[] resources = new PathMatchingResourcePatternResolver().getResources(path);
          for (Resource resource : resources) {
            log.info("resource: {}", String.valueOf(resource));
            MetadataReader reader = factory.getMetadataReader(resource);
            log.info("ClassName:{}", reader.getClassMetadata().getClassName());
            if (reader.getAnnotationMetadata().hasAnnotation(Component.class.getName())
                    || reader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName())) {
              AbstractBeanDefinition definition = BeanDefinitionBuilder
                      .genericBeanDefinition(reader.getClassMetadata().getClassName())
                      .getBeanDefinition();
              if (beanFactory instanceof DefaultListableBeanFactory context) {
                String name = nameGenerator.generateBeanName(definition, context);
                context.registerBeanDefinition(name, definition);
              }

            }
          }
        }
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}
